package common.hibernate.service.impl;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;

import org.hibernate.Criteria;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Projections;
import org.hibernate.transform.Transformers;
import org.springframework.dao.DataAccessException;

import common.hibernate.service.BaseService;
import common.hibernate.vo.Page;

public class BaseServiceImpl implements BaseService {

	private SessionFactory sessionFactory;

	@Override
	public Session getSession() {
		return sessionFactory.getCurrentSession();
	} 
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#load(java.lang.Class,
	 * java.io.Serializable)
	 */
	@Override
	public <T> T load(Class<T> entityClass, Serializable id) throws DataAccessException {
		return (T) this.getSession().load(entityClass, id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#get(java.lang.Class,
	 * java.io.Serializable)
	 */
	@Override
	public <T> T get(Class<T> entityClass, Serializable id) throws DataAccessException {
		return (T) this.getSession().get(entityClass, id);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#save(T)
	 */
	@Override
	public <T> Serializable save(T entity) throws DataAccessException {
		return this.getSession().save(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#saveOrUpdate(T)
	 */
	@Override
	public <T> void saveOrUpdate(T entity) throws DataAccessException {
		this.getSession().saveOrUpdate(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#update(T)
	 */
	@Override
	public <T> void update(T entity) throws DataAccessException {
		this.getSession().update(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#delete(T)
	 */
	@Override
	public <T> void delete(T entity) throws DataAccessException {
		this.getSession().delete(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#refresh(T)
	 */
	@Override
	public <T> void refresh(T entity) throws DataAccessException {
		this.getSession().refresh(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#merge(T)
	 */
	@Override
	public <T> T merge(T entity) throws DataAccessException {
		return (T) this.getSession().merge(entity);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#clear()
	 */
	@Override
	public void clear() throws DataAccessException {
		this.getSession().clear();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#flush()
	 */
	@Override
	public void flush() throws DataAccessException {
		this.getSession().flush();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#loadAll(java.lang.Class)
	 */
	@Override
	public <T> List<T> loadAll(Class<T> entityClass) throws DataAccessException {
		Criteria criteria = this.getSession().createCriteria(entityClass);
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		return criteria.list();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#find(java.lang.String,
	 * java.lang.Object)
	 */
	@Override
	public <T> List<T> find(String queryString, Object... values) throws DataAccessException {
		Query queryObject = this.getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		return queryObject.list();
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#find(java.lang.String,
	 * java.lang.Object)
	 */
	@Override
	public <T> List<T> findByParamList(String queryString, List<Object> list, Object[] values) throws DataAccessException {
		Query queryObject = this.getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		if(null != list && list.size() > 0) {
			queryObject.setParameterList("list", list);
		}
		return queryObject.list();
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#find(java.lang.String,
	 * java.lang.Object)
	 */
	@Override
	public <T> T findOne(String queryString, Object... values) throws DataAccessException {
		Query queryObject = this.getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		List<T> list = queryObject.list();
		if(list.size() > 0) {
			return list.get(0);
		}
		return null;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#queryTotal(java.lang.String,
	 * java.lang.Object)
	 */
	@Override
	public int queryTotal(final String queryString, final Object... values) throws DataAccessException {
		Query queryObject = this.getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		Long num = (Long) queryObject.uniqueResult();
		return num == null ? 0 : num.intValue();
	}
	
	/*
	 * (non-Javadoc)
	 * 
	 * @see com.oraro.mrj.rbac.service.impl.BaseService#find(java.lang.String,
	 * int, int, java.lang.Object)
	 */
	@Override
	public  <T> Page<T> find(final String hql, final String countHql,  final int currentPage, final int pageSize,
			final Object... values) throws DataAccessException {
			Page<T> page = new Page<>();
			page.setPageSize(pageSize);
			page.setCurrentPage(currentPage);
			Query queryObject = this.getSession().createQuery(hql);
			if (values != null) {
				for (int i = 0; i < values.length; i++) {
					queryObject.setParameter(i, values[i]);
				}
			}
			page.setTotal(this.queryTotal(countHql, values));
			queryObject.setFirstResult(page.getStart());
			queryObject.setMaxResults(page.getPageSize());
			page.setRows(queryObject.list());
			return page;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.oraro.mrj.rbac.service.impl.BaseService#findByExample(java.lang.Object
	 * )
	 */
	@Override
	public <T> List<T> findByExample(Object exampleEntity) throws DataAccessException {
		return (List<T>) this.findByExample(null, exampleEntity, -1, -1);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.oraro.mrj.rbac.service.impl.BaseService#findByExample(java.lang.String
	 * , java.lang.Object, int, int)
	 */
	@Override
	public <T> List<T> findByExample(String entityName, Object exampleEntity, int firstResult, int maxResults)
			throws DataAccessException {
		Session session = this.getSession();
		Criteria executableCriteria = (entityName != null ?
				session.createCriteria(entityName) : session.createCriteria(exampleEntity.getClass()));
		executableCriteria.add(Example.create(exampleEntity));
		if (firstResult >= 0) {
			executableCriteria.setFirstResult(firstResult);
		}
		if (maxResults > 0) {
			executableCriteria.setMaxResults(maxResults);
		}
		return executableCriteria.list();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.oraro.mrj.rbac.service.impl.BaseService#findByExample(java.lang.String
	 * , int, int, java.lang.Object)
	 */
	@Override
	public <T> Page<T> findByExample(final String entityName, final int currentPage, final int pageSize,
			final Object exampleEntity) throws DataAccessException {
		Session session = this.getSession();
		Page<T> page = new Page<T>();
		page.setPageSize(pageSize);
		page.setCurrentPage(currentPage);
		Criteria executableCriteria = (entityName != null ? session.createCriteria(entityName) : session
				.createCriteria(exampleEntity.getClass()));
		executableCriteria.add(Example.create(exampleEntity));
		int totalCount = ((Long) executableCriteria.setProjection(Projections.rowCount()).uniqueResult())
				.intValue();
		page.setTotal(totalCount);
		executableCriteria.setProjection(null);
		executableCriteria.setFirstResult(page.getStart());
		executableCriteria.setMaxResults(page.getPageSize());
		page.setRows(executableCriteria.list());
		return page;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.oraro.mrj.rbac.service.impl.BaseService#findByCriteria(org.hibernate
	 * .criterion.DetachedCriteria)
	 */
	@Override
	public <T> List<T> findByCriteria(DetachedCriteria criteria) throws DataAccessException {
		return (List<T>) this.findByCriteria(criteria, -1, -1);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.oraro.mrj.rbac.service.impl.BaseService#findByCriteria(org.hibernate
	 * .criterion.DetachedCriteria, int, int)
	 */
	@Override
	public <T> List<T> findByCriteria(DetachedCriteria criteria, int firstResult, int maxResults)
			throws DataAccessException {
		Criteria executableCriteria = criteria.getExecutableCriteria(this.getSession());
		if (firstResult >= 0) {
			executableCriteria.setFirstResult(firstResult);
		}
		if (maxResults > 0) {
			executableCriteria.setMaxResults(maxResults);
		}
		return executableCriteria.list();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.oraro.mrj.rbac.service.impl.BaseService#findByCriteriaForPage(org
	 * .hibernate.criterion.DetachedCriteria, int, int)
	 */
	@Override
	public <T> Page<T> findByCriteriaForPage(final DetachedCriteria criteria, final int currentPage, final int pageSize)
			throws DataAccessException {
		Page<T> page = new Page<T>();
		page.setPageSize(pageSize);
		page.setCurrentPage(currentPage);
		Criteria executableCriteria = criteria.getExecutableCriteria(this.getSession());
		int totalCount = ((Long) executableCriteria.setProjection(Projections.rowCount()).uniqueResult())
				.intValue();
		page.setTotal(totalCount);
		executableCriteria.setProjection(null);
		executableCriteria.setFirstResult(page.getStart());
		executableCriteria.setMaxResults(page.getPageSize());
		page.setRows(executableCriteria.list());
		return page;
	}

	@Override
	public void dbOperation(Object[] saves, Object[] updates, Object[] deletes)
			throws DataAccessException {
		
		if(null != deletes) {
			for (Object d : deletes) {
				this.getSession().delete(d);
			}
		}
		
		if(null != saves) {
			for (Object s : saves) {
				this.getSession().save(s);
			}
		}
		if(null != updates) {
			for (Object u : updates) {
				this.getSession().update(u);
			}
		}
	}
	@Override
	public void dbOperation(String hql, Object[] values, Object[] saves, Object[] updates, Object[] deletes)
			throws DataAccessException {
		this.executeUpdate(hql, values);
		this.dbOperation(saves, updates, deletes);
	}

	@Override
	public int dbOperationForSql(String sql , Object[] values){
		Session session = this.getSession();
		SQLQuery sqlQuery = session.createSQLQuery(sql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				sqlQuery.setParameter(i, values[i]);
			}
		}
		return sqlQuery.executeUpdate();
	}
	
	@Override
	public <T> void save(T... entities) throws DataAccessException {
		if(null != entities) {
			for (T t : entities) {
				this.getSession().save(t);		
			}
		}
	}

	@Override
	public <T> void update(T... entities) throws DataAccessException {
		if(null != entities) {
			for (T t : entities) {
				this.getSession().update(t);		
			}
		}
	}

	@Override
	public <T> void delete(T... entities) throws DataAccessException {
		if(null != entities) {
			for (T t : entities) {
				this.getSession().delete(t);		
			}
		}
	}
	
	@Override
	public <T> Page<T> queryForSql(String sql , String countSql, int currentPage, int pageSize, Object ... values) throws DataAccessException {
		Page<T> page = new Page<T>();
		page.setPageSize(pageSize);
		page.setCurrentPage(currentPage);
		SQLQuery queryCount = this.getSession().createSQLQuery(countSql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryCount.setParameter(i, values[i]);
			}
		}
		Integer totalCount = ((Number)queryCount.uniqueResult()).intValue();
		page.setTotal(totalCount);
		SQLQuery query = this.getSession().createSQLQuery(sql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		query.setFirstResult(page.getStart());
		query.setMaxResults(page.getPageSize());
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); 
		page.setRows(query.list());
		return page;
	}
	

	@Override
	public List<Map<String, Object>> queryForSql(String sql, Object[] values) throws DataAccessException {
		SQLQuery query = this.getSession().createSQLQuery(sql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				query.setParameter(i, values[i]);
			}
		}
		query.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP); 
		return query.list();
	}

	@Override
	public void executeUpdate(String hql, Object... values) throws DataAccessException {
		Query createQuery = this.getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				createQuery.setParameter(i, values[i]);
			}
		}
		createQuery.executeUpdate();
	}

	@Override
	public <T> void executeUpdateForListParamter(String hql, String param, Collection<?> collection, Object... values)
			throws DataAccessException {
		Query createQuery = this.getSession().createQuery(hql);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				createQuery.setParameter(i, values[i]);
			}
		}
		createQuery.setParameterList(param, collection);
		createQuery.executeUpdate();
	}

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}


}
